package message;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class Node {

	private int nodeId;
	private int[] neighbours;
	private int[] neighboursDistance;
	private int[][] distanceTable;
	private List<DistanceEstimation> distanceVector;
	public static final int MAX_VALUE = Integer.MAX_VALUE / 2;
	
	public Node(int routerId, int[] neighbours, int[] neighboursDistance, int routersCount){
		this.nodeId = routerId;
		this.neighbours = neighbours;
		this.neighboursDistance = neighboursDistance;
		this.distanceTable = new int[routersCount + 1][neighbours.length];
		this.distanceVector = new LinkedList<DistanceEstimation>();
		
		initRouterTable();
	}
	
	public int getId(){
		return this.nodeId;
	}
	
	public void cleanDistanceVector() {
		this.distanceVector.clear();
	}
	
	public void initRouterTable(){
		for (int i = 1; i < distanceTable.length; i++)
			for (int j = 1; j < neighbours.length; j++)
				distanceTable[i][j] = MAX_VALUE;
		
		for (int i = 1; i < neighbours.length; i++) {
			distanceTable[neighbours[i]][i] = neighboursDistance[i];
			distanceTable[nodeId][i] = 0; // menor dist�ncia dele com ele mesmo
		}
		
		for (int i = 1; i < neighbours.length; i++) {
			DistanceEstimation node = new DistanceEstimation(nodeId, neighbours[i], neighboursDistance[i]);
			this.distanceVector.add(node);
		}
	}
	
	public void receiveDistanceVector(List<DistanceEstimation> distanceVector) {
		
		System.out.println("--");
		printDistanceVector();
		
		for (DistanceEstimation distanceEstimation : distanceVector) {
			
			int neighbourId = distanceEstimation.getId();
			int neighbourDistance = distanceEstimation.getDistance();
			int destinationId = distanceEstimation.getDestinationId();
			
			if (getMinDistance(destinationId) > getDistance(neighbourId) + neighbourDistance) {
				distanceTable[destinationId][getNeighbourIndex(neighbourId)] = getDistance(neighbourId) + neighbourDistance;
				
				this.distanceVector.add(
						new DistanceEstimation(nodeId, destinationId, distanceTable[destinationId][getNeighbourIndex(neighbourId)]));
			} else if (getMinNeighbourIndex(destinationId) == neighbourId) {
				distanceTable[destinationId][getNeighbourIndex(neighbourId)] = getDistance(neighbourId) + neighbourDistance;
				
				this.distanceVector.add(
						new DistanceEstimation(nodeId, destinationId, getMinDistance(destinationId)));
			}
		}
		
		printDistanceVector();
		
	}
	
	private void printDistanceVector() {
		for (int i = 0; i < distanceTable.length; i++) {
			int[] line = distanceTable[i];
			System.out.println(Arrays.toString(line));
		}
		System.out.println();
	}

	private int getDistance(int neighbourId) {
		return neighboursDistance[getNeighbourIndex(neighbourId)];
	}
	
	private int getNeighbourIndex(int neighbourId) {
		for (int i = 1; i < neighbours.length; i++)
			if (neighbours[i] == neighbourId)
				return i;
		return MAX_VALUE;
	}

	private int getMinDistance(int destinationId) {
		return distanceTable[destinationId][getMinNeighbourIndex(destinationId)];
	}

	public List<DistanceEstimation> getDistanceVector() {
		return new LinkedList<DistanceEstimation>(distanceVector);
	}

	public void setNeighbourFailure(Integer neighbourId) {
//		distanceTable[neighbourId][getNeighbourIndex(neighbourId)] = MAX_VALUE;
		
		int minDistance = getMinDistance(neighbourId);
		this.distanceVector.add(new DistanceEstimation(nodeId, neighbourId, minDistance));
	}

	private int getMinNeighbourIndex(Integer destinationId) {
		int min = MAX_VALUE;
		
		int[] destinationRow = distanceTable[destinationId];
		int minNeighbourIndex = MAX_VALUE;
		
		for (int i = 1; i < destinationRow.length; i++)
			if (min >= destinationRow[i]) {
				min = destinationRow[i];
				minNeighbourIndex = i;
			}
		
		return minNeighbourIndex;
	}
	
	public int getMinNeighbourId(int destinationId) {
		return neighbours[getMinNeighbourIndex(destinationId)];
	}
	
	public List<DistanceEstimation> getBestEstimation() {
		
		LinkedList<DistanceEstimation> bestEstimationVector = new LinkedList<DistanceEstimation>();
		
		for (int i = 1; i < distanceTable.length; i++) {
			
			if (i == nodeId) continue;
			
			bestEstimationVector.add(new DistanceEstimation(nodeId, i, getMinDistance(i)));
			
		}
		
		return bestEstimationVector;
	}

	public boolean isNeighbour(int destinationId) {
		for (int i = 1; i < neighbours.length; i++) {
			if (neighbours[i] == destinationId) return true;
		}
		return false;
	}
}